﻿namespace Universe.HpglParser
{
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.IO;
    using VectorImage;


    public class HpglReader
    {
        struct Point
        {
            public int X, Y;
        }

        enum Commands
        {
            Unknown,
            SelectPen,
            PenUp,
            PenDown,
        }

        public static IEnumerable<SimpleLine> Parse(TextReader reader)
        {
            if (reader == null)
                throw new ArgumentNullException("reader");

            Point prev = new Point();
            int pen = 1;
            foreach (var rawCommand in ParseCommands(reader))
            {
                Commands command;
                int[] parameters;
                if (TryParseCommand(rawCommand, out command, out parameters))
                {
                    if (command == Commands.SelectPen && parameters.Length == 1)
                        pen = parameters[0];

                    else if (command == Commands.PenUp && parameters.Length == 2)
                    {
                        prev.X = parameters[0];
                        prev.Y = parameters[1];
                    }

                    else if (command == Commands.PenDown && parameters.Length > 0 && parameters.Length % 2 == 0)
                    {
                        for (int i = 0; i < parameters.Length; i += 2)
                        {
                            Point next = new Point()
                            {
                                X = parameters[i], 
                                Y = parameters[i + 1]
                            };

                            SimpleLine line = new SimpleLine
                            {
                                X1 = prev.X,
                                Y1 = prev.Y,
                                X2 = next.X,
                                Y2 = next.Y,
                                Pen = pen,
                            };

                            prev = next;
                            yield return line;
                        }
                    }
                }
            }
        }

        // SP, PU & PD Commands are enough
        static bool TryParseCommand(string arg, out Commands command, out int[] parameters)
        {
            command = Commands.Unknown;
            parameters = null;

            if (arg == null || arg.Length < 3)
                return false;

            command = Commands.Unknown;
            var sub = arg.Substring(0, 2);
            if (sub == "SP")
            {
                command = Commands.SelectPen;
            }
            else if (sub == "PU")
            {
                command = Commands.PenUp;
            }
            else if (sub == "PD")
            {
                command = Commands.PenDown;
            }

            if (command == Commands.Unknown)
                return false;

            var arr = arg.Substring(2).Split(' ', ',');
            var ints = new int[arr.Length];
            for (int i = 0; i < arr.Length; i++)
            {
                int local;
                if (!int.TryParse(arr[i], out local))
                {
                    command = Commands.Unknown;
                    return false;
                }

                ints[i] = local;
            }

            parameters = ints;
            return true;
        }
        
        private static IEnumerable<string> ParseCommands(TextReader reader)
        {
            StringBuilder cmd = new StringBuilder();
            
            while (true)
            {
                int charInt = reader.Read();
                if (charInt == -1)
                    break;

                char ch = (char) charInt;
                if (ch == ';')
                {
                    if (cmd.Length > 0)
                        yield return cmd.ToString();

                    cmd.Clear();
                }
                else if (ch != '\r' && ch != '\n')
                    cmd.Append(ch);
            }
        }
    }
}
